﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Numerics;

namespace Inspired.Euler
{
    public static class Extensions
    {
        public static double Sqrt5 = Math.Sqrt(5);
        public static double Phi = (1 + Extensions.Sqrt5) / 2;
        public static double phi = (1 - Extensions.Sqrt5) / 2;

        public static long Product(this IEnumerable<int> factors)
        {
            return factors.Aggregate(1, (accum, factor) => accum *= factor);
        }

        public static IEnumerable<T> Display<T>(this IEnumerable<T> arg)
        {
            Console.WriteLine(String.Join(", ", arg.ToArray()));
            return arg;
        }

        public static T DisplayAndPause<T>(this T result)
        {
            Console.WriteLine(result);
            Console.ReadLine();
            return result;
        }

        public static IEnumerable<long> Fibonacci()
        {
            long a = 1;
            long b = 2;
            long t = 0;

            yield return a;
            yield return b;

            while (true)
            {
                yield return t = a + b;
                a = b;
                b = t;
            }
        }

        public static IEnumerable<long> Divisors(this long number)
        {
            Dictionary<long, object> divisors = new Dictionary<long, object>();
            foreach (int candidate in Enumerable.Range(1, (int)(Math.Sqrt(number) + 1)))
            {
                if (number % candidate == 0)
                {
                    if (!divisors.ContainsKey(candidate)) divisors.Add(candidate, null);
                    if (!divisors.ContainsKey(number / candidate)) divisors.Add(number / candidate, null);
                }
            }
            return divisors.Select(d => d.Key);
        }

        public static IEnumerable<int> Primes(this int number)
        {
            int sievebound = (number - 1) / 2; // last index of sieve
            bool[] sieve = new bool[sievebound + 1];
            int crosslimit = (int)((Math.Sqrt(number) - 1) / 2);
            for (int i = 1; i <= crosslimit; i++)
            {
                if (!sieve[i]) // 2*i+1 is prime, mark multiples
                {
                    for (int j = 2 * i * (i + 1); j <= sievebound; j += 2 * i + 1)
                        sieve[j] = true;
                }
            }

            yield return 2;
            for (int i = 1; i < sievebound; i++)
            {
                if (!sieve[i])
                    yield return 2 * i + 1;
            }

            /*
            List<int> primes = new List<int>();
            List<int> nums = new List<int>() { 2 };

            for (int i = 3; i <= number; i += 2)
            {
                nums.Add(i);
            }

            while (nums.Count > 0)
            {
                int nextPrime = nums[0];
                nums.RemoveAt(0);
                for (int i = nextPrime * nextPrime; i <= number; i += nextPrime)
                {
                    nums.Remove(i);
                }
                yield return nextPrime;
            }
            */
        }

        public static bool IsPrime(this int number)
        {
            return ((long)number).IsPrime();
        }

        public static bool IsPrime(this long number)
        {
            if(number < 2) return false;
            if(number < 4) return true;         //2 and 3 are prime
            if(number % 2 == 0) return false;
            if(number < 9) return true;         //we have already excluded 4,6 and 8.
            if(number % 3 == 0) return false;
    
            double f = 5;
            double r = Math.Floor(Math.Sqrt(number)); // rounded to the greatest integer r so that r*r<=n
            while(f<=r)
            {
                if (number % f == 0) return false;
                if (number % (f + 2) == 0) return false;
                f = f + 6;
            }
            return true;
        }

        public static long Reverse(this long number)
        {
            long reverse = number % 10;
            number = number / 10;
            while (number != 0)
            {
                reverse = (number % 10) + (10 * reverse);
                number = number / 10;
            }
            return reverse;

            //string original = Convert.ToString(number);
            ////char[] value = original.ToCharArray(); Array.Reverse(value);
            //return Int64.Parse(new String(original.ToCharArray().Reverse().ToArray()));
        }

        public static bool IsPalindrome(this int number, int toBase)
        {
            string original = Convert.ToString(number, toBase);
            char[] value = original.ToCharArray(); Array.Reverse(value);
            return original == new String(value);
        }

        public static bool IsPalindrome(this BigInteger number)
        {
            return number == number.Reverse();
        }

        public static BigInteger Reverse(this BigInteger number)
        {
            BigInteger reverse = number % 10;
            number = number / 10;
            while (number != 0)
            {
                reverse = (number % 10) + (10 * reverse);
                number = number / 10;
            }
            return reverse;
        }


        //public static IEnumerable<int> Primes007()
        //{
        //    List<int> primes = new List<int>();
        //    for (int i = 2; ; i++)
        //    {
        //        if (primes.Any(prime => i % prime == 0))
        //            continue;

        //        primes.Add(i);
        //        yield return i;
        //    }
        //}

        public static IEnumerable<long> GetPrimes()
        {
            yield return 2;
            List<long> primes = new List<long>() { 2 };
            for (long i = 3; ; i += 2)
            {
                if (primes.Any(prime => i % prime == 0))
                    continue;

                primes.Add(i);
                //Console.Title = i.ToString();
                yield return i;
            }
        }

        public static IEnumerable<long> GetTriangle012()
        {
            long triangle = 0;
            long current = 1;
            while (true)
            {
                triangle += current++;
                yield return triangle;
            }
        }

        public static BigInteger Factorial(this int seed)
        {
            if (seed == 0) return 1;
            return Enumerable.Range(1, seed).Aggregate(new BigInteger(1), (accum, item) => accum *= item);
        }

        public static BigInteger Combinatorics(int n, int r)
        {
            return n.Factorial() / (r.Factorial() * (n - r).Factorial());
        }

        public static IEnumerable<long> AsEnumerable(this long number)
        {
            while (number != 0)
            {
                yield return number % 10;
                number /= 10;
            }
        }

        public static long ToInt64(this IEnumerable<long> numbers)
        {
            return numbers
                .Select((item, index) => item * (long)Math.Pow(10, index))
                .Sum();
        }

        public static bool IsPandigital(this IEnumerable<long> sequence)
        {
            return 
                ! sequence
                    .OrderBy(number => number)
                    .Where((item, index) => item != (index + 1))
                    .Any();
        }
    }
}
